/***************************************************************************************
* Copyright (c) 2014-2024 Zihao Yu, Nanjing University
*
* NEMU is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*          http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/

#include "sdb.h"

#define NR_WP 32

typedef struct watchpoint {
  int NO;
  struct watchpoint *next;
  paddr_t addr;
  /* TODO: Add more members if necessary */
} WP;

static WP wp_pool[NR_WP] = {};
static WP *head = NULL, *free_ = NULL;

void init_wp_pool() {
  int i;
  for (i = 0; i < NR_WP; i ++) {
    wp_pool[i].NO = i;
    wp_pool[i].next = (i == NR_WP - 1 ? NULL : &wp_pool[i + 1]);
  }

  head = NULL;
  free_ = wp_pool;
}


/* TODO: Implement the functionality of watchpoint */

void add_watchpoint(paddr_t addr) {
  WP *wp;
  if (free_ == NULL) {
    printf("No enough space for watchpoint.\n");
    assert(0);
  }
  if(addr < 0x80000000 || addr > 0x80000000 + 0x8000000){
    printf("Invalid address.\n");
    return;
  }
  wp = free_;
  free_ = free_->next;
  wp->addr = addr;
  wp->next = head;
  head = wp;
  printf("Add watchpoint %d at address 0x%08x\n", wp->NO, wp->addr);
}

void del_watchpoint(int NO) {
  WP *wp, *last;
  for (wp = head, last = NULL; wp != NULL; last = wp, wp = wp->next) {
    if (wp->NO == NO) {
      if (last == NULL) {
        head = wp->next;
      } else {
        last->next = wp->next;
      }
      wp->next = free_;
      free_ = wp;
      printf("Delete watchpoint %d at address 0x%08x\n", wp->NO, wp->addr);
      return;
    }
  }
  printf("Watchpoint %d not found.\n", NO);
}

bool is_watchpoint_hit(paddr_t addr) 
{
  WP *wp;
  for (wp = head; wp != NULL; wp = wp->next) {
    if (wp->addr == addr) {
      return true;
    }
  }
  return false;
}

bool display_watchpoint() {
  WP *wp;
  if (head == NULL) {
    printf("No watchpoint.\n");
    return false;
  }
  printf("NO\tAddress\n");
  for (wp = head; wp != NULL; wp = wp->next) {
    printf("%d\t0x%08x\n", wp->NO, wp->addr);
  }
  return true;
}